<!DOCTYPE HTML>
<html>

<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  
  <title>部门内部培训 react 系列讲义二：使用 React 重写 kede mobile | DeepThought</title>
  <meta name="google-site-verification" content="Ei6mmDdf-cJwox1RvJOeX8Dc720fqWjZQu-Ww2BjBt4">
  
  <meta name="author" content="积慕">
  
  <meta name="description" content="2018年公司内部培训讲义,react深入浅出,react相关工具,create-react-app使用">
  
  
  <meta name="keywords" content="react,react入门,react深入浅出,上手react,create-react-app,react脚手架,jsx,styled-components">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  
  <meta property="og:title" content="部门内部培训 react 系列讲义二：使用 React 重写 kede mobile">
  <meta property="og:site_name" content="DeepThought">

  
  <meta property="og:image" content>
  

  <link href="https://pic.zhuliang.ltd/favicon.ico" rel="shortcut icon" type="image/x-icon">
  <link rel="alternate" href="/atom.xml" title="DeepThought" type="application/atom+xml">
  <link rel="stylesheet" href="/css/style.css" media="screen" type="text/css">

  <link rel="stylesheet" href="//at.alicdn.com/t/font_1187293_8fec2ou5fyx.css">

  <!-- <link rel="stylesheet" href="//cdn.bootcss.com/highlight.js/9.6.0/styles/github.min.css"> -->
  <!-- <script src="//cdn.bootcss.com/highlight.js/9.6.0/highlight.min.js"></script> -->
  <script src="/js/highlight.min.js"></script>

  <link rel="stylesheet" href="/css/markdown.css">

  <script src="/js/av-min.3.0.4.js"></script>
  <script src="/js/Valine.min.1.3.6.js"></script>

</head></html>

<body>
  <header id="header" class="inner"><div class="alignleft">
  <h1><a href="/">DeepThought</a></h1>
  <h2><a href="/">知识需沉淀，更需分享</a></h2>
</div>
<nav id="main-nav" class="alignright">
  <ul>
    
      <li><a href="/">首页</a></li>
    
      <li><a href="/about">关于我</a></li>
    
      <li><a href="/atom.xml">订阅RSS</a></li>
    
  </ul>
  <div class="clearfix"></div>
</nav>
<div class="clearfix"></div>
        </header>
  <div id="content" class="inner">
    <div id="main-col" class="alignleft"><div id="wrapper"><article class="post">
  
  <div class="post-content">
    <header>
      
        <div class="icon"></div>
      

 
      
  
    <h1 class="title">部门内部培训 react 系列讲义二：使用 React 重写 kede mobile</h1>
  


      
        <time datetime="2018-11-16T10:29:23.000Z">
          create: 2018-11-16 18:29:23 | update: 2018-11-16 18:29:23
            <span style="float: right;">
    <span class="post-count" id="busuanzi_container_page_pv" style="display: inline;">
        
        本文总阅读量: <span id="busuanzi_value_page_pv"></span>&nbsp;次
        <span class="blueSeparate">&nbsp;|&nbsp;</span>
        
        <span class="post-count">文章总字数:&nbsp;7.3k&nbsp;字</span>
    <span class="blueSeparate">&nbsp;|&nbsp;</span>
    <span class="post-count">阅读约需:&nbsp;35&nbsp;分钟</span>
    <br>
</span>
</span>
        </time>
      
            
    </header>
    <div class="entry">
      
      <blockquote>
<ul>
<li>react 官网：<a href="https://reactjs.org/" target="_blank" rel="noopener">https://reactjs.org/</a></li>
<li>此文为公司内部培训用 react 讲义内容，实战部分，用 react 改写原 H5 手机端页面，适合想学习 react，入门 react 的同学阅读，代码请参加 github：<a href="https://github.com/KedeIT/kede.mobile" target="_blank" rel="noopener">https://github.com/KedeIT/kede.mobile</a><a id="more"></a>
</li>
</ul>
</blockquote>
<h1 id="项目初始化："><a href="#项目初始化：" class="headerlink" title="项目初始化："></a>项目初始化：</h1><h2 id="使用-create-react-app-创建项目"><a href="#使用-create-react-app-创建项目" class="headerlink" title="使用 create-react-app 创建项目"></a>使用 create-react-app 创建项目</h2><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">create-react-app kede.mobile
</code></pre>
<h2 id="删除多余的文件："><a href="#删除多余的文件：" class="headerlink" title="删除多余的文件："></a>删除多余的文件：</h2><p>如：</p>
<ul>
<li>src/registerServiceWorker.js<ul>
<li>能够允许你在第一次访问成功之后，本地会有完整的缓存（相当于把网页当成App了）。第二次再访问的时候，就算断网了，依然可以打开页面</li>
</ul>
</li>
<li>src/logo.svg</li>
<li>src/index.css</li>
<li>src/App.test.js<br>删除这些文件，需要调整 src/App.css 以及 src/index.js 对这些文件的引用。</li>
</ul>
<h2 id="创建开发所需文件夹"><a href="#创建开发所需文件夹" class="headerlink" title="创建开发所需文件夹"></a>创建开发所需文件夹</h2><p>在src文件夹中创建 components 文件夹，所有的组件均建立在该文件夹下</p>
<h1 id="styled-components：react中的CSS最佳实践"><a href="#styled-components：react中的CSS最佳实践" class="headerlink" title="styled-components：react中的CSS最佳实践"></a>styled-components：react中的CSS最佳实践</h1><p><img src="https://pic.zhuliang.ltd/ac66c00d-a6df-4642-a9c6-0de546d388c9.png-c" alt></p>
<blockquote>
<p>styled-components：</p>
<ul>
<li>核心理念：移除样式与组件之间的对应关系</li>
<li>css in js，写的是真CSS，非类似于 CSS 的 JS 对象</li>
<li>拥有 sass，less 的优点</li>
</ul>
<p>官网地址：<a href="https://www.styled-components.com/" target="_blank" rel="noopener">https://www.styled-components.com/</a></p>
</blockquote>
<h2 id="使用-styled-components-全局注入-normalize-css"><a href="#使用-styled-components-全局注入-normalize-css" class="headerlink" title="使用 styled-components 全局注入 normalize.css"></a>使用 styled-components 全局注入 normalize.css</h2><h3 id="安装-styled-components："><a href="#安装-styled-components：" class="headerlink" title="安装 styled-components："></a>安装 styled-components：</h3><ul>
<li>编写本文档时，对应使用的版本号为3.4.9</li>
<li>目前最新的v4版本已经弃用 injectGlobal，改用createGlobalStyle，具体见此： <a href="https://www.styled-components.com/docs/api#createglobalstyle" target="_blank" rel="noopener">https://www.styled-components.com/docs/api#createglobalstyle</a></li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">npm install --save styled-components@3.4.9
</code></pre>
<h3 id="创建-src-style-js文件"><a href="#创建-src-style-js文件" class="headerlink" title="创建 src/style.js文件"></a>创建 src/style.js文件</h3><p>使用 {injectGlobal} from ‘styled-components’进行全局样式的注入<br><em>推荐使用 normalize.css?v=7.0.0 版本</em><br>normalize.css：<a href="https://github.com/necolas/normalize.css" target="_blank" rel="noopener">https://github.com/necolas/normalize.css</a></p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">/style.js
import {injectGlobal} from &#39;styled-components&#39;;
injectGlobal`
    /*normalize.css*/
`
</code></pre>
<h3 id="在-src-index-js-中引入上面的style-js"><a href="#在-src-index-js-中引入上面的style-js" class="headerlink" title="在 src/index.js 中引入上面的style.js"></a>在 src/index.js 中引入上面的style.js</h3><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import &#39;./style.js&#39;;
</code></pre>
<h1 id="网站body样式："><a href="#网站body样式：" class="headerlink" title="网站body样式："></a>网站body样式：</h1><p>/style.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">export const Wrapper = styled.div`
    max-width: 750px;
    min-width: 320px;
    margin: 0 auto;
    padding-top: 0;
    overflow: hidden;
    position: relative;
    padding-bottom: 80px;
`
</code></pre>
<p>/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import {
    Wrapper
} from &#39;./style.js&#39;;

const container = (
    &lt;Wrapper&gt;
        &lt;App /&gt;
    &lt;/Wrapper&gt;
)
ReactDOM.render(container, document.getElementById(&#39;root&#39;));
</code></pre>
<h1 id="header-组件布局"><a href="#header-组件布局" class="headerlink" title="header 组件布局"></a>header 组件布局</h1><p>路径：src/components/header</p>
<p>新增文件：</p>
<ul>
<li>index.js：用于页面布局。</li>
<li>style.js：页面样式，使用”styled-components”模块进行布局处理。</li>
</ul>
<p>/components/header/style.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import styled from &#39;styled-components&#39;;
import searchIcon from &#39;../../assets/img/icon_head.png&#39;;
export const HeaderWrapper = styled.div`
    width: 100%;
    max-width: 750px;
    min-width: 320px;
    height: 45px;
    z-index: 11;
    top: 0;
    position: fixed;
`
export const Mask = styled.div`
    width: 100%;
    height: 45px;
    position: absolute;
    background: #141414;
    opacity: .8;
    top: 0;
    left: 0;
    z-index: 0;
`

export const ClassMenuLink = styled.a`
    position: absolute;
    top: 50%;
    left: 10px;
    width: 31px;
    margin-top: -16px;
    display: block;
`
export const ClassMenuImg = styled.img.attrs({
    src: &quot;https://pic.keede.com//app/images/Community/Index/classmenu.png&quot;
})`
    width: 31px;
    vertical-align: middle;
    border: none;
`

export const SearchBar = styled.div`
    background-size: 21px auto;
    background-position: 12px -47px;
    background-color: #fff;
    background-repeat: no-repeat;
    width: 60%;
    height: 27px;
    margin: 9px auto;
    padding: 0 24px 0 42px;
    position: relative;
    border-radius: 15px;
    &amp;.head_icon {
        background-image: url(${searchIcon});
    }
    &amp;.hd_search{
        background-size: 21px auto;
        background-position: 12px -47px;
        background-color: #ffffff;
        background-repeat: no-repeat;
        width: 60%;
        height: 27px;
        margin: 9px auto;
        padding: 0 24px 0 42px;
        position: relative;
        border-radius: 15px;
    }
`

export const SearchInput = styled.input.attrs({
    placeholder: &quot;库博硅水凝胶日抛&quot;
})`
    display: inline;
    height: 27px;
    line-height: initial;
    width: 100%;
    border: none;
    background: none;
    font-size: 14px;
    outline:none;
`
</code></pre>
<p>/components/header/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React, { PureComponent } from &#39;react&#39;
import {
    HeaderWrapper,
    Mask,
    ClassMenuLink,
    ClassMenuImg,
    SearchBar,
    SearchInput
} from &#39;./style&#39;;
class Header extends PureComponent {
    render() {
        return (&lt;HeaderWrapper&gt;
            &lt;Mask&gt;&lt;/Mask&gt;
            &lt;ClassMenuLink&gt;
                &lt;ClassMenuImg&gt;&lt;/ClassMenuImg&gt;
            &lt;/ClassMenuLink&gt;
            &lt;SearchBar className=&quot;head_icon hd_search&quot;&gt;
                &lt;SearchInput&gt;&lt;/SearchInput&gt;
            &lt;/SearchBar&gt;
        &lt;/HeaderWrapper&gt;);
    }
}

export default Header;
</code></pre>
<h1 id="footer组件布局"><a href="#footer组件布局" class="headerlink" title="footer组件布局"></a>footer组件布局</h1><p>路径：src/components/footer</p>
<p>新增文件：</p>
<ul>
<li>index.js：用于页面布局</li>
<li>style.js：页面样式，使用 “styled-components” 模块进行布局处理。</li>
</ul>
<p>/components/footer/style.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import styled from &#39;styled-components&#39;;
import footerIcon from &#39;../../assets/img/footer_nav.png&#39;
export const FooterWrapper = styled.div`
    background: #ffffff;
    border-top: 1px solid #ccc;
    height: 55px;
    position: fixed;
    bottom: 0;
    max-width: 750px;
    min-width: 320px;
    width: 100%;
    font-size: 0;
    z-index: 11;
    transform: translateX(-50%);
    left: 50%;
`
export const ItemContainer = styled.ul`
    margin: 0;
    padding: 0;
    border: 0;
    outline: 0;
    font-size: 100%;
    vertical-align: baseline;
    background: transparent;
    -webkit-text-size-adjust: none;
    -webkit-font-smoothing: antialiased;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
`

export const Item = styled.li`
    width: 25%;
    display: inline-block;
    position: relative;
    float: left;
    /* 元素内部元素上的样式 */
    .link_duwu::after{
        background-position: 0 -100px;
    }
`

export const ItemLink = styled.a`
    display: block;
    width: 100%;
    height: 100%;
    text-align: center;
    position: relative;
    vertical-align: text-bottom;
    font-size: 14px;
    padding: 31px 0 3px;
    text-decoration: none;
    cursor: pointer;
    color: #888;
    -webkit-tap-highlight-color: rgba(255,255,255,0);
    &amp;::after{
        content: &quot;&quot;;
        background: url(${footerIcon});
        background-size: 20px auto;
        width: 20px;
        height: 20px;
        display: block;
        position: absolute;
        top: 6px;
        left: 50%;
        margin-left: -10px;
    }
    &amp;.link_home::after{
        background-position: 0 0px;
    }

    &amp;.link_cart::after{
        background-position: 0 -120px;
    }

    &amp;.link_mine::after{
        background-position: 0 -140px;
    }
`

export const CartQuantity = styled.i`
    background: #ff7c7c;
    font-size: 10px;
    padding: 0 5px;
    border-radius: 8px;
    position: absolute;
    top: 5px;
    left: 50%;
    margin-left: 4px;
    font-style: normal;
    color: #fff;
`
</code></pre>
<p>/components/footer/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React, { PureComponent } from &#39;react&#39;
import {
    FooterWrapper,
    ItemContainer,
    Item,
    ItemLink,
    CartQuantity
} from &#39;./style&#39;;
class Footer extends PureComponent {
    render() {
        return (
            &lt;FooterWrapper&gt;
                &lt;ItemContainer&gt;
                    &lt;Item&gt;
                        &lt;ItemLink className=&quot;link_home&quot;&gt;首页&lt;/ItemLink&gt;
                    &lt;/Item&gt;
                    &lt;Item&gt;
                        &lt;ItemLink className=&quot;link_duwu&quot;&gt;毒物&lt;/ItemLink&gt;
                    &lt;/Item&gt;
                    &lt;Item&gt;
                        &lt;ItemLink className=&quot;link_cart&quot;&gt;购物车&lt;/ItemLink&gt;
                        &lt;CartQuantity&gt;0&lt;/CartQuantity&gt;
                    &lt;/Item&gt;
                    &lt;Item&gt;
                        &lt;ItemLink className=&quot;link_mine&quot;&gt;我的&lt;/ItemLink&gt;
                    &lt;/Item&gt;
                &lt;/ItemContainer&gt;
            &lt;/FooterWrapper&gt;
        );
    }
}

export default Footer;
</code></pre>
<h1 id="使用iconfont-cn"><a href="#使用iconfont-cn" class="headerlink" title="使用iconfont.cn"></a>使用iconfont.cn</h1><ol>
<li>从 iconfont.cn 中下载需要的图标到本地（红框中的为项目中需要使用到的文件）</li>
</ol>
<ul>
<li><img src="https://pic.zhuliang.ltd/1101407-20180928143924004-1701288675.png-c" alt></li>
</ul>
<ol start="2">
<li>嵌入 react 项目中</li>
</ol>
<ul>
<li><img src="https://pic.zhuliang.ltd/1101407-20180928155024427-378426499.png-c" alt></li>
</ul>
<ol start="3">
<li>修改文件后缀： iconfont.css -&gt; iconfont.js </li>
<li>修改 iconfont.js 中内容，使用 styled-components</li>
</ol>
<ul>
<li>使用unicode<br><img src="https://pic.zhuliang.ltd/1101407-20180928155706017-714559447.png-c" alt><br>组件中使用：<br><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><pre><code class="javascript">导入：import  from &#39;./assets/icon/iconfont.js&#39;
使用：&lt;i class=&quot;iconfont&quot;&gt;&amp; #xe67c;&lt;/i&gt;  //中间空格去掉
</code></pre>
</li>
<li>使用fontClass<br><img src="https://pic.zhuliang.ltd/1101407-20180928175857212-868804250.png-c" alt></li>
</ul>
<p>组件中使用：</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">导入：import{CartIcon} from &#39;./assets/icon/iconfont.js&#39;
使用：&lt;CartIcon&gt;&lt;/CartIcon&gt;
</code></pre>
<h1 id="使用-react-router-dom-进行页面路由"><a href="#使用-react-router-dom-进行页面路由" class="headerlink" title="使用 react-router-dom 进行页面路由"></a>使用 react-router-dom 进行页面路由</h1><blockquote>
<p>文档：<a href="https://reacttraining.com/react-router/web/guides/basic-components" target="_blank" rel="noopener">https://reacttraining.com/react-router/web/guides/basic-components</a></p>
</blockquote>
<h2 id="安装："><a href="#安装：" class="headerlink" title="安装："></a>安装：</h2><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">npm install --save react-router-dom
</code></pre>
<h2 id="使用："><a href="#使用：" class="headerlink" title="使用："></a>使用：</h2><p>App.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { BrowserRouter, Route } from &#39;react-router-dom&#39;;
class App extends Component {
  render() {
    return (
      &lt;div className=&quot;App&quot;&gt;
        &lt;BrowserRouter&gt;
          &lt;Fragment&gt;
            &lt;Header&gt;&lt;/Header&gt;

            &lt;Route path=&quot;/&quot; exact component={Index}&gt;&lt;/Route&gt;
            &lt;Route path=&quot;/Duwu&quot; exact component={Duwu}&gt;&lt;/Route&gt;
            &lt;Route path=&quot;/Cart&quot; exact component={Cart}&gt;&lt;/Route&gt;
            &lt;Route path=&quot;/Mine&quot; exact component={Mine}&gt;&lt;/Route&gt;
            &lt;Route path=&quot;/Detail&quot; exact component={Detail}&gt;&lt;/Route&gt;

            &lt;Footer /&gt;
          &lt;/Fragment&gt;
        &lt;/BrowserRouter&gt;
      &lt;/div&gt;
    );
  }
}
</code></pre>
<blockquote>
<p>需要注意，Fragment标签需要有一个根节点</p>
<p>Route所用与的属性：path，exact，render，component等。</p>
</blockquote>
<p>在styled-components中，使用Link模块</p>
<p>/components/footer/style.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import {Link} from &#39;react-router-dom&#39;;
export const ItemLink = styled(Link).attrs({
    to:props=&gt;props.to  //从外部获取标签上 to 元素属性的方法
})`
    /*其他样式*/
`
</code></pre>
<h2 id="页面使用："><a href="#页面使用：" class="headerlink" title="页面使用："></a>页面使用：</h2><p>通过 \&lt;Link>标签进行路由跳转</p>
<blockquote>
<p>Link所用到的属性：to。</p>
</blockquote>
<p>这里使用在 styled-components声明的标签 ItemLink  （此时即为Link）</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">&lt;ItemLink className=&quot;link_cart&quot; to=&quot;/cart&quot;&gt;购物车&lt;/ItemLink&gt;
</code></pre>
<h2 id="标签切换时的当前状态变化"><a href="#标签切换时的当前状态变化" class="headerlink" title="标签切换时的当前状态变化"></a>标签切换时的当前状态变化</h2><h1 id="首页内容渲染"><a href="#首页内容渲染" class="headerlink" title="首页内容渲染"></a>首页内容渲染</h1><p>涉及知识点：</p>
<ul>
<li>axios 使用</li>
<li>在测试环境，可以直接在 public 文件夹下新建一个 api 文件夹，将一些 mock 数据放到里面</li>
<li>dangerouslySetInnerHTML</li>
</ul>
<p>封装的axios（beta）</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import qs from &#39;qs&#39;
import axios from &#39;axios&#39;

/**
 * axios请求拦截器
 * @param {object} config axios请求配置对象
 * @return {object} 请求成功或失败时返回的配置对象或者promise error对象
 **/
axios.interceptors.request.use(config =&gt; {
  return config
}, error =&gt; {
  return Promise.reject(error)
})

/**
 * axios 响应拦截器
 * @param {object} response 从服务端响应的数据对象或者error对象
 * @return {object} 响应成功或失败时返回的响应对象或者promise error对象
 **/
axios.interceptors.response.use(response =&gt; {
  return response
}, error =&gt; {
  return Promise.resolve(error)
})

/**
 * 请求发出后检查返回的状态码,统一捕获正确和错误的状态码，正确就直接返回response,错误就自定义一个返回对象
 * @param {object} response 响应对象
 * @return {object} 响应正常就返回响应数据否则返回错误信息
 **/
function checkStatus (response) {
  // 如果状态码正常就直接返回数据,这里的状态码是htttp响应状态码有400，500等，不是后端自定义的状态码
  if (response &amp;&amp; ((response.status === 200 || response.status === 304 || response.status === 400))) {
    return response.data // 直接返回http response响应的data,此data会后端返回的数据数据对象，包含后端自定义的code,message,data属性
  }
  return { // 自定义网络异常对象
    code: &#39;404&#39;,
    message: &#39;网络异常&#39;
  }
}

/**
 * 检查完状态码后需要检查后如果成功了就需要检查后端的状态码处理网络正常时后台语言返回的响应
 * @param {object} res 是后台返回的对象或者自定义的网络异常对象，不是http 响应对象
 * @return {object} 返回后台传过来的数据对象，包含code,message,data等属性，
 **/
// function checkCode (res) {
//   // 如果状态码正常就直接返回数据
//   if (res.code === -404) { // 这里包括网络异常，服务器异常等这种异常跟业务无关，直接弹窗警告
//     alert(res.message)
//     return {code: &#39;&#39;, message: &#39;网络错误&#39;}
//   } else { // 除了上面的异常就剩下后端自己返回的状态code了这个直接返回出去供调用时根据不同的code做不同的处理
//     return res
//   }
// }

export default {
  post (url, data) {
    return axios({
      method: &#39;post&#39;,
      baseURL: process.env.BASE_URL,
      url: url,
      data: qs.stringify(data),
      headers: {
        &#39;X-Requested-With&#39;: &#39;XMLHttpRequest&#39;,
        &#39;Content-Type&#39;: &#39;application/x-www-form-urlencoded; charset=UTF-8&#39;
      },
      timeout: 10000
    }).then((res) =&gt; {
      return checkStatus(res)
    })
  },
  get (url, params) {
    return axios({
      method: &#39;get&#39;,
      baseURL: process.env.BASE_URL,
      url,
      params,
      timeout: 10000,
      headers: {
        &#39;X-Requested-With&#39;: &#39;XMLHttpRequest&#39;
      }
    }).then(
      (response) =&gt; {
        return checkStatus(response)
      }
    )
  }

}
</code></pre>
<p>/pages/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React,{Component} from &#39;react&#39;;
import http from &#39;../../service/api&#39;;

class Index extends Component{
    state = {
        content : &quot;Index page&quot;
    }
    render(){
        return &lt;div style={{width:"600px", margin:"50% auto"}} dangerouslySetInnerHTML={{__html:this.state.content}}&gt;
        &lt;/div&gt;
    }

    componentDidMount(){
        http.get(&quot;/api/feature.json&quot;).then(ent=&gt;{
            this.setState(()=&gt;{
                return {
                    content:ent.data
                }
            })
        });
    }
}

export default Index;
</code></pre>
<h1 id="GoToTop"><a href="#GoToTop" class="headerlink" title="GoToTop"></a>GoToTop</h1><p>/components/goToTop/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React,{PureComponent} from &#39;react&#39;;

class GoToTop extends PureComponent{
    render(){
        return null;
    }

    componentDidMount(){
        window.scrollTo(0,0);
    }
}

export default GoToTop;
</code></pre>
<blockquote>
<p>使用方法：直接当组件一样，在需要的页面中进行加载即可。</p>
</blockquote>
<h1 id="scrollRestoration"><a href="#scrollRestoration" class="headerlink" title="scrollRestoration"></a>scrollRestoration</h1><ul>
<li>通过 sessionStorage 来记录位置</li>
<li>无法通过组件的形式来进行实现（∵部分页面是后渲染—即页面加载完成之后再去请求获取模板数据进行渲染）<br>/common/utility.js</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">const getWindowScroll = () =&gt; {
    let supportPageOffset = window.pageXOffset !== undefined;
    let isCSS1Compat = ((document.compatMode || &quot;&quot;) === &quot;CSS1Compat&quot;);
    let x = supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft;
    let y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;
    return {
        x,
        y
    };
}

const getWindowScrollFromSession = (path) =&gt; {
    let info = sessionStorage.getItem(`scroll-${path}`);
    if (!info) return null;
    return JSON.parse(info);
}

export const setWindowScrollToSession = (path) =&gt; {
    let scrollInfo = getWindowScroll();
    sessionStorage.setItem(`scroll-${path}`, JSON.stringify(scrollInfo));
}

export const autoWindowScroll = (path) =&gt; {
    let scrollInfo = getWindowScrollFromSession(path);
    if (!scrollInfo) return;
    window.scrollTo(scrollInfo.x, scrollInfo.y);
}
</code></pre>
<p>页面使用：</p>
<blockquote>
<p>需要注意的是，这里需要使用setState异步函数形式下的回调函数</p>
</blockquote>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">componentDidMount(){
    http.get(&quot;/api/feature.json&quot;).then(ent=&gt;{
        this.setState(()=&gt;{
            return {
                content:ent.data
            }
        }, ()=&gt;autoWindowScroll(this.props.location.pathname))
    });
}
componentWillUnmount(){
    setWindowScrollToSession(this.props.location.pathname);
}
</code></pre>
<h1 id="组件之间的数据传递："><a href="#组件之间的数据传递：" class="headerlink" title="组件之间的数据传递："></a>组件之间的数据传递：</h1><p>一般情况下，组件之间的数据传递是通过 props 进行，那么如果现在有 OrderItem 被嵌套很深，但该子组件 OrderItem 的一些父组件可能并不会用到 OrderItem 所需要的一些属性，但是因为嵌套了子组件 OrderItem，就不得不对外索要这些子组件所需要的属性，如下：</p>
<p><img src="https://pic.zhuliang.ltd/20c3ae3e-e513-48d9-93c9-36df02ede3bc.png-c" alt="20c3ae3e-e513-48d9-93c9-36df02ede3bc.png"></p>
<p>在上图中，假设子组件 OrderItem 需要用到 index.js 中 state 中的某个属性值A，那么这个时候，虽然 OrderList.js 用不到属性值A，但它必须分别对父级提供 props.A 属性，以此来将A属性传递给子组件 OrderItem。</p>
<p>那么有没有什么办法可以简化这个操作？通过：context。</p>
<p>举例：有如下文件结构：</p>
<p><img src="https://pic.zhuliang.ltd/b5f84f92-2e48-450c-aed8-263552947a12.png-c" alt="b5f84f92-2e48-450c-aed8-263552947a12.png"></p>
<p>需要通过 index.js 中的按钮来控制 OrderItem.js 中的文本颜色：引用上：index.js 中嵌套 OrderList.js，OrderList.js 中嵌套 OrderItem.js，在正常单项数据流的情况下，虽然最终只是 OrderItem 用到了由  index.js传递的属性（itemColor），但因为 index.js 并非直接引用 OrderItem.js，所以作为中间者的 OrderList.js 不得不对 index.js 提供 props.itemColor，从而再将该值传递给 OrderItem.js 中的 props.itemColor。代码如下：</p>
<p>index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import OrderList from &#39;./components/OrderList&#39;;

    this.state = {
        orderList: [
            {
                title: &quot;博士伦隐形眼镜&quot;,
                price: 23
            },
            {
                title: &quot;海昌隐形眼镜&quot;,
                price: 33
            },
            {
                title: &quot;依视路镜片&quot;,
                price: 43
            }
        ],
        currentColor: style.colorRed
    }
    render() {
        return (
            &lt;div style={{ height: "600px", margin: "50% auto" }}&gt;
                &lt;div&gt;Mine page&lt;/div&gt;
                &lt;OrderList 
                    orderList={this.state.orderList} 
                    itemColor={this.state.currentColor}&gt;
                &lt;/OrderList&gt;
                &lt;button onClick={() =&gt; this.toggleColor()}&gt;toggleColor&lt;/button&gt;
            &lt;/div&gt;);
    }
</code></pre>
<p>orderList.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import OrderItem from &#39;./OrderItem&#39;;
export default (props) =&gt; {
    return (
        &lt;OrderListWrapper&gt;
            &lt;OrderTitle&gt;订单信息：&lt;/OrderTitle&gt;
            {props.orderList.map((item, index) =&gt; {
                    return (&lt;OrderItem 
                            key={index} 
                            item={item} 
                            itemColor={props.itemColor}&gt;
                        &lt;/OrderItem&gt;)
            })}
        &lt;/OrderListWrapper&gt;
    )
}
</code></pre>
<p>orderItem.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">export default (props)=&gt;{
    return (
        &lt;OrderItem color={props.itemColor}&gt;
            {props.item.title}
        &lt;/OrderItem&gt;
    )
}
</code></pre>
<h2 id="使用-react-的-context-来改写上面的例子："><a href="#使用-react-的-context-来改写上面的例子：" class="headerlink" title="使用 react 的 context 来改写上面的例子："></a>使用 react 的 context 来改写上面的例子：</h2><p>在src目录下增加context文件夹：</p>
<p><img src="https://pic.zhuliang.ltd/46fd1f50-b17c-47ac-aa45-773e4261b926.png-c" alt="46fd1f50-b17c-47ac-aa45-773e4261b926.png"></p>
<p>context/orderItemContext.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React from &#39;react&#39;;

export const style = {
    colorRed: &quot;red&quot;,
    colorBlue: &quot;blue&quot;
}

export default React.createContext(style.colorRed);
</code></pre>
<p>pages/mine/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React, { Component } from &#39;react&#39;
import OrderList from &#39;./components/OrderList&#39;;

import OrderItemContext, { style } from &#39;../../context/orderItemContext&#39;;
class Mine extends Component {

    constructor(props) {
        super(props);
        this.state = {
            orderList: [
                {
                    title: &quot;博士伦隐形眼镜&quot;,
                    price: 23
                },
                {
                    title: &quot;海昌隐形眼镜&quot;,
                    price: 33
                },
                {
                    title: &quot;依视路镜片&quot;,
                    price: 43
                }
            ],
            currentColor: style.colorRed
        }
    }

    render() {
        return (
            &lt;div style={{ height: "600px", margin: "50% auto" }}&gt;
                &lt;div&gt;Mine page&lt;/div&gt;

                &lt;OrderItemContext.Provider value={this.state.currentColor}&gt;
                    &lt;OrderList orderList={this.state.orderList} &gt;&lt;/OrderList&gt;
                &lt;/OrderItemContext.Provider&gt;
                &lt;button onClick={() =&gt; this.toggleColor()}&gt;toggleColor&lt;/button&gt;
            &lt;/div&gt;);
    }

    toggleColor = () =&gt; {
        if (this.state.currentColor === style.colorRed) {
            this.setState({
                currentColor: style.colorBlue
            });
        } else {
            this.setState(() =&gt; ({
                currentColor: style.colorRed
            }));
        }
    }

}

export default Mine;
</code></pre>
<p>pages/mine/components/OrderList.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React from &#39;react&#39;;
import {
    OrderListWrapper,
    OrderTitle
} from &#39;../style&#39;;
import OrderItem from &#39;./OrderItem&#39;;
export default (props) =&gt; {
    return (
        &lt;OrderListWrapper&gt;
            &lt;OrderTitle&gt;订单信息：&lt;/OrderTitle&gt;
            {props.orderList.map((item, index) =&gt; {
                return &lt;OrderItem key={index} item={item} &gt;&lt;/OrderItem&gt;
            })}
        &lt;/OrderListWrapper&gt;
    )
}
</code></pre>
<p>pages/mine/components/OrderItem.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React from &#39;react&#39;;
import OrderItemContext from &#39;../../../context/orderItemContext&#39;;
import {
    OrderItem
} from &#39;../style&#39;;
export default (props) =&gt; {
    return (
        &lt;OrderItemContext.Consumer&gt;
            {
                importedValue =&gt; {
                    return (
                        &lt;OrderItem color={importedValue}&gt;
                            {props.item.title}
                        &lt;/OrderItem&gt;
                    )
                }
            }
        &lt;/OrderItemContext.Consumer&gt;
    )
}
</code></pre>
<ul>
<li>通过React的Context，OrderItem.js中原先用到的props.currentColor，直接通过OrderItemContext.Consumer来获取。而数据则直接由index.js中的OrderItemContext.Provider来提供。原先作为中间者的OrderList.js则不再无谓传输props.itemColor</li>
<li>关于React的上下文：<ul>
<li>创建上下文：let MyContext = React.createContext([defaultValue])<ul>
<li>它有一个默认值，关于这个默认值，仅在Consumer往上查找，找不到Provider的时候才会触发（而非Provider组件的value属性为null/undefined时触发）</li>
</ul>
</li>
<li>提供数据：通过使用MyContext.Provider来限定上下文的范围，通过 value属性来传递值。</li>
<li>获取/使用数据：通过使用MyContext.Consumer来限定上下文使用的范围，在写法上需要注意下，children部分以代码段开始。</li>
<li>更多关于上下文见此：<a href="https://reactjs.org/docs/context.html" target="_blank" rel="noopener">https://reactjs.org/docs/context.html</a></li>
</ul>
</li>
</ul>
<h2 id="使用-react-redux-来传递数据"><a href="#使用-react-redux-来传递数据" class="headerlink" title="使用 react-redux 来传递数据"></a>使用 react-redux 来传递数据</h2><h3 id="使用步骤："><a href="#使用步骤：" class="headerlink" title="使用步骤："></a>使用步骤：</h3><ol>
<li>安装</li>
</ol>
<p></p><p class="code-caption" data-lang="shell" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="shell">npm install --save react-redux
同时也需要安装 redux 模块：
npm install --save redux
</code></pre>
<ol start="2">
<li>在项目根目录下创建store文件夹，并在其内创建reducer.js，index.jS</li>
</ol>
<p><img src="https://pic.zhuliang.ltd/1101407-20180926160444487-702449478.png-c" alt></p>
<p>/store/reducer.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">const defaultState = {
    ...
}
export default (state = defaultState, action) =&gt; {
    switch (action.type) {
        ...
        default:
            return state;
    }
}
</code></pre>
<p>/store/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { createStore } from &#39;redux&#39;;
import reducer from &#39;./reducer&#39;;
const store = createStore(reducer)
export default store;
</code></pre>
<ol start="3">
<li>在入口文件中引入 Provider组件，作为顶层App的分发点，在相关的页面组件中使用connect进行组件跟redux的store进行连接。</li>
<li>/index.js</li>
</ol>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { Provider } from &#39;react-redux&#39;;
import store from &#39;./store&#39;;
const container = (
    &lt;Provider store={store}&gt;
        &lt;Wrapper&gt;
            &lt;App /&gt;
        &lt;/Wrapper&gt;
    &lt;/Provider&gt;
)
ReactDOM.render(container, document.getElementById(&#39;root&#39;));
</code></pre>
<ol start="4">
<li>components/自定义组件/index.js</li>
</ol>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import {connect} from &#39;react-redux&#39;;
...
class Header extends Component {
    render() {
        ...
    }
}
...
export default connect(null,null)(Header);
</code></pre>
<h3 id="例子：使用-react-redux-实现点击按钮时互换上方-title-和-content-的文字颜色："><a href="#例子：使用-react-redux-实现点击按钮时互换上方-title-和-content-的文字颜色：" class="headerlink" title="例子：使用 react-redux 实现点击按钮时互换上方 title 和 content 的文字颜色："></a>例子：使用 react-redux 实现点击按钮时互换上方 title 和 content 的文字颜色：</h3><p><img src="https://pic.zhuliang.ltd/9b306fb8-e888-4d8f-a4f0-1524d83bac00.gif" alt="9b306fb8-e888-4d8f-a4f0-1524d83bac00.gif"></p>
<p>/store/reducer.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">const defaultState = {
    title: {
        text: &quot;here is duwu title&quot;,
        color: &quot;red&quot;
    },
    content: {
        text: &quot;here is duwu content&quot;,
        color: &quot;blue&quot;
    }
}

export default (state = defaultState, action) =&gt; {
    switch (action.type) {
        case &quot;reverseColor&quot;:
            {
                let currentTitle = state.title;
                let currentContent = state.content;
                return {
                    title: {
                        ...currentTitle,
                        color: currentContent.color
                    },
                    content: {
                        ...currentContent,
                        color: currentTitle.color
                    }
                }

            }
        default:
            return state;
    }
}
</code></pre>
<p>/store/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { createStore } from &#39;redux&#39;;
import reducer from &#39;./reducer&#39;;

const store = createStore(reducer)

export default store;
</code></pre>
<p>/pages/duwu/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React, { PureComponent } from &#39;react&#39;
import { Link } from &#39;react-router-dom&#39;;
import { connect } from &#39;react-redux&#39;;
class Duwu extends PureComponent {
    render() {
        const { title, content, reverseColor } = this.props;
        return (
            &lt;div style={{ height: "600px", margin: "50% auto" }}&gt;
                Duwu page
                &lt;Link to=&quot;/cart&quot;&gt;跳转到购物车&lt;/Link&gt;
                &lt;br&gt;&lt;/br&gt;
                &lt;br&gt;&lt;/br&gt;
                &lt;br&gt;&lt;/br&gt;
                &lt;div style={{ color: title.color, }}&gt;{title.text}&lt;/div&gt;
                &lt;div style={{ color: content.color }}&gt;{content.text}&lt;/div&gt;
                &lt;button onClick={()=&gt;reverseColor()}&gt;反转颜色&lt;/button&gt;
            &lt;/div&gt;)
    }
}

const mapStateToProps = (state) =&gt; {
    return {
        title: state.title,
        content: state.content
    }
}

const mapDispatchToProps = (dispatch) =&gt; {
    return {
        reverseColor() {
            let action = {
                type: &quot;reverseColor&quot;,
            }
            dispatch(action);
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Duwu);
</code></pre>
<h1 id="redux"><a href="#redux" class="headerlink" title="redux"></a>redux</h1><h2 id="redux-简介："><a href="#redux-简介：" class="headerlink" title="redux 简介："></a>redux 简介：</h2><ul>
<li>redux 是一个应用架构。其设计理念：所有的数据放在 store 里管理，一个组件改变了store中的内容，其他组件就会感知到store的这个变化，从而直接从store中获取数据来进行更新。</li>
<li>redux = reducer + flux</li>
</ul>
<h3 id="什么是flux？"><a href="#什么是flux？" class="headerlink" title="什么是flux？"></a>什么是flux？</h3><ul>
<li>facebook 用于构建客户端Web应用程序的一个应用架构，利用单向数据流来补充 react 的可组合视图组件。与其说是一个架构，不如说是一个模式。</li>
</ul>
<h3 id="flux-工作流："><a href="#flux-工作流：" class="headerlink" title="flux 工作流："></a>flux 工作流：</h3><p><img src="https://pic.zhuliang.ltd/939114dc-0d18-4a7e-a88b-ee6b96c461ae.png-c" alt="939114dc-0d18-4a7e-a88b-ee6b96c461ae.png"></p>
<h3 id="redux-工作流："><a href="#redux-工作流：" class="headerlink" title="redux 工作流："></a>redux 工作流：</h3><p><img src="https://pic.zhuliang.ltd/1101407-20180925141104597-2088633436.png-c" alt></p>
<ul>
<li>Redux 的灵感来源于 Flux 的几个重要特性。和 Flux 一样，Redux 规定，<strong>模型的更新逻辑全部集中于一个特定的层（Flux 里的 store，Redux 里的 reducer）</strong>。</li>
<li>Redux 和 Flux 都不允许程序直接修改数据，而是用一个叫作 “action” 的<strong>普通对象</strong>来对更改进行描述。</li>
</ul>
<h2 id="为什么要用-redux？"><a href="#为什么要用-redux？" class="headerlink" title="为什么要用 redux？"></a>为什么要用 redux？</h2><p>react 的数据传递是单向的：</p>
<p><img src="https://pic.zhuliang.ltd/14a288ea-7104-4afb-b3dd-53832a3d753f.gif" alt></p>
<blockquote>
<p>通过props属性进行数据的传递</p>
</blockquote>
<p>非父子组件之间共享 state</p>
<p><img src="https://pic.zhuliang.ltd/61f0b857-ff20-447e-a854-b4047f3c6c20.gif" alt></p>
<p>使用redux之后：数据传递不再是单向、线性的，所有组件的数据都会放到 store 中，直接下放到对应需要更新的组件中。</p>
<p><img src="https://pic.zhuliang.ltd/fa9a3b6a-31be-4011-a87a-fb11a19b979f.gif" alt></p>
<h2 id="使用-react-redux-来简化"><a href="#使用-react-redux-来简化" class="headerlink" title="使用 react-redux 来简化"></a>使用 react-redux 来简化</h2><p>项目地址：<a href="https://github.com/reduxjs/react-redux" target="_blank" rel="noopener">https://github.com/reduxjs/react-redux</a></p>
<blockquote>
<p>react-redux 是一个模块（或者说是一个库：一个将 redux 模式跟 react.js相结合的一个库）（也可以认为是 redux 在 react.js 中的体现）</p>
</blockquote>
<h1 id="如何进一步优化使用：react-redux"><a href="#如何进一步优化使用：react-redux" class="headerlink" title="如何进一步优化使用：react-redux"></a>如何进一步优化使用：react-redux</h1><h2 id="将抽离-action-types，将-action-的生成放到一个独立的文件中，进一步从页面中剥离出业务。"><a href="#将抽离-action-types，将-action-的生成放到一个独立的文件中，进一步从页面中剥离出业务。" class="headerlink" title="将抽离 action.types，将 action 的生成放到一个独立的文件中，进一步从页面中剥离出业务。"></a>将抽离 action.types，将 action 的生成放到一个独立的文件中，进一步从页面中剥离出业务。</h2><ol>
<li>在 /store 文件夹中新增 actionTypes.js，actionCreators.js</li>
</ol>
<p><img src="https://pic.zhuliang.ltd/1ad8708a-76f0-43d4-ba98-ff1e6a19643f.png-c" alt="1ad8708a-76f0-43d4-ba98-ff1e6a19643f.png"></p>
<p>actionTypes.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">export const REVERSE_COLOR = &quot;DUWU/REVERSE_COLOR&quot;
</code></pre>
<p>actionCreators.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { REVERSE_COLOR } from &#39;./actionTypes&#39;;
export const GetReverseColorAction = ()=&gt;{
    return {
        type:REVERSE_COLOR
    }
}
</code></pre>
<ol start="2">
<li>调整原 reducer.js 和 /pages/duwu/index.js代码</li>
</ol>
<p>/store/reducer.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import {REVERSE_COLOR} from &#39;./actionTypes&#39;;
const defaultState = {
    title: {
        text: &quot;here is duwu title&quot;,
        color: &quot;red&quot;
    },
    content: {
        text: &quot;here is duwu content&quot;,
        color: &quot;blue&quot;
    }
}
export default (state = defaultState, action) =&gt; {
    switch (action.type) {
        case REVERSE_COLOR: //统一从actionTypes中获取
            {
                let currentTitle = state.title;
                let currentContent = state.content;
                return {
                    title: {
                        ...currentTitle,
                        color: currentContent.color
                    },
                    content: {
                        ...currentContent,
                        color: currentTitle.color
                    }
                }
            }
        default:
            return state;
    }
}
</code></pre>
<p>/pages/duwu/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import React, { PureComponent } from &#39;react&#39;
import { setWindowScrollToSession, autoWindowScroll } from &#39;../../common/utility&#39;;
import { connect } from &#39;react-redux&#39;; //引入 connect 模块
import { InitIndex } from &#39;./store/actionCreators&#39;; //引入InitIndex 方法
class Index extends PureComponent {
    render() {
        return &lt;div dangerouslySetInnerHTML={{ __html: this.props.content }}&gt;&lt;/div&gt;
    }
    componentDidMount() {
        this.props.initPage(this.props.location.pathname);
    }
    componentWillUnmount() {
        setWindowScrollToSession(this.props.location.pathname);
    }
}
const mapStateToProps = (state) =&gt; {
    return {
        content: state.getIn([&quot;index&quot;, &quot;content&quot;])
    }
}
const mapDispatchToProps = (dispatch) =&gt; {
    return {
        initPage(pathname) {
            dispatch(InitIndex()); //dispatch中使用的是方法，而非对象
            autoWindowScroll(pathname);
        }
    }
}
export default connect(mapStateToProps, mapDispatchToProps)(Index);
</code></pre>
<h2 id="immutable-js"><a href="#immutable-js" class="headerlink" title="immutable.js"></a>immutable.js</h2><p>为什么要用 immutable.js？</p>
<ul>
<li>在 reducer.js 中，无法对state直接做修改，只能返回一个新的对象用来更新 state，如果是基于原始 state 的修改，那么只能对他进行深拷贝后进行修改，再进行返回。</li>
<li>通过使用 immutable.js，就可以省略深拷贝这一步，因为任何对于 immutable 对象的修改，最终都会返回一个新的 immutable 对象。<h3 id="使用-immutable"><a href="#使用-immutable" class="headerlink" title="使用 immutable"></a>使用 immutable</h3>安装：</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">npm install immutable
</code></pre>
<p>在 reducer.js 中引入 immutable，将原先的 state 对象转换为 immutable 类型</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { REVERSE_COLOR } from &#39;./actionTypes&#39;;
import { fromJS } from &#39;immutable&#39;; //引入 immutable的 fromJS 模块
//fromJS()：将一个map对象转换成 immutable 对象
const defaultState = fromJS({
    title: {
        text: &quot;here is duwu title&quot;,
        color: &quot;red&quot;
    },
    content: {
        text: &quot;here is duwu content&quot;,
        color: &quot;blue&quot;
    }
})

export default (state = defaultState, action) =&gt; {
    switch (action.type) {
        case REVERSE_COLOR:
            {
                /* 方式一：直接返回新对象 */
                // let currentTitle = state.title;
                // let currentContent = state.content;
                // return {
                //     title: {
                //         ...currentTitle,
                //         color: currentContent.color
                //     },
                //     content: {
                //         ...currentContent,
                //         color: currentTitle.color
                //     }
                // }

                /* 方式二：使用深拷贝 */
                // let currentTitleColor = state.title.color;
                // let currentContentColor = state.content.color;
                // let deepCopyInfo = JSON.parse(JSON.stringify(state));
                // deepCopyInfo.title.color=currentContentColor;
                // deepCopyInfo.content.color=currentTitleColor;
                // return deepCopyInfo;

                /* 方式三：使用immutable对象 */
                //使用getIn()以层级的关系获取数据
                let currentTitleColor = state.getIn([&quot;title&quot;,&quot;color&quot;]);
                let currentContentColor = state.getIn([&quot;content&quot;,&quot;color&quot;]);
                //使用setIn()以层级的方式来设置属性
                return state
                        .setIn([&quot;title&quot;,&quot;color&quot;],currentContentColor)
                        .setIn([&quot;content&quot;,&quot;color&quot;],currentTitleColor)

                /* DO NOT DO THIS：不能直接修改state */
                // console.log(state);
                // state.title.color = currentContent.color;
                // return state;
            }
        default:
            return state;
    }
}
</code></pre>
<p>调整使用的地方：/pages/duwu/index.js</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">const mapStateToProps = (state) =&gt; {
    return {
        //使用get来获取属性，使用 toJS()将immutable对象转换成js对象
        title: state.get(&quot;title&quot;).toJS(),
        content: state.get(&quot;content&quot;).toJS()
    }
}
</code></pre>
<p>更多关于immutable.js：<a href="https://github.com/facebook/immutable-js" target="_blank" rel="noopener">https://github.com/facebook/immutable-js</a></p>
<h2 id="进一步抽离-store-文件夹到每一个独立的-pages-页面中"><a href="#进一步抽离-store-文件夹到每一个独立的-pages-页面中" class="headerlink" title="进一步抽离 store 文件夹到每一个独立的 pages 页面中"></a>进一步抽离 store 文件夹到每一个独立的 pages 页面中</h2><h3 id="调整项目结构"><a href="#调整项目结构" class="headerlink" title="调整项目结构"></a>调整项目结构</h3><ul>
<li>在 /pages/duwu 文件夹中新建 store 文件夹</li>
<li>将原 /store 目录下的 actionCreators.js，actionTypes.js 移动至 /pages/duwu/store中</li>
<li>将原 /store 目录下的 reducer.js 复制一份到 /pages/duwu/store 中</li>
<li>在 /pages/duwu/store 中新建一个 index.js</li>
</ul>
<p><strong>调整后的最终目录结构为：</strong></p>
<p><img src="https://pic.zhuliang.ltd/14a2224c-a26e-44a7-bf2b-ff85e1c5fe05.png-c" alt="14a2224c-a26e-44a7-bf2b-ff85e1c5fe05.png"></p>
<p>其中:</p>
<ul>
<li><p>/pages/duwu/store 文件夹中的 actionCreators.js，actionTypes.js，reducer.js 代码保持不变。</p>
</li>
<li><p>/pages/duwu/store/index.js：仅作为对外暴露的入口</p>
</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import reducer from &#39;./reducer&#39;;
import * as actionCreators from &#39;./actionCreators&#39;;
import * as actionTypes from &#39;./actionTypes&#39;;

export { reducer, actionCreators, actionTypes};
</code></pre>
<ul>
<li>/store/index.js：保持不变</li>
<li>/store/reducer.js：引入 combineReducers 模块 来连接不同页面、组件的 store</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { combineReducers } from &#39;redux&#39;;
import { reducer as duwuReducer } from &#39;../pages/duwu/store&#39;;
export default combineReducers({
    duwu: duwuReducer  // &quot;标记A&quot;
});
</code></pre>
<ul>
<li>/pages/duwu/index：调整原先获取数据的地方</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">/*
    注意：原先是直接 state.get() 来获取数据
    使用 combineReducers 之后，state 的结构有所变化，需要加上在 /store/reducer.js 中定义 key，如在上面(/sotre/reducer.js)标识的&quot;标记A&quot; 处的 key值
*/
const mapStateToProps = (state) =&gt; {
    return {
        //获取值的时候，需要加上 duwu 这个 js 对象，之后在 duwu 这个对象上来进行数据的获取。
        title: state.duwu.get(&quot;title&quot;).toJS(),
        content: state.duwu.get(&quot;content&quot;).toJS()
    }
}
</code></pre>
<h2 id="统一格式，让在页面中使用-state-duwu-也是一个-immutable对象"><a href="#统一格式，让在页面中使用-state-duwu-也是一个-immutable对象" class="headerlink" title="统一格式，让在页面中使用 state.duwu 也是一个 immutable对象"></a>统一格式，让在页面中使用 state.duwu 也是一个 immutable对象</h2><ul>
<li>通过使用 redux-immutable 来实现。</li>
<li>redux-immutable 的作用是？<ul>
<li>创建一个 redux combineReducers 的等效函数，以便能够跟 immutable.js的state 协同工作</li>
</ul>
</li>
<li>安装：</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">npm install redux-immutable
</code></pre>
<ul>
<li>调整 /store/reducer.js</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { combineReducers } from &#39;redux-immutable&#39;; //原先从 redux 模块获取改成从 redux-immutable获取
import { reducer as duwuReducer } from &#39;../pages/duwu/store&#39;;
export default combineReducers({
    duwu: duwuReducer
});
</code></pre>
<ul>
<li>调整 /pages/duwu/index.jS</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">const mapStateToProps = (state) =&gt; {
    return {
        // title: state.duwu.get(&quot;title&quot;).toJS(),
        // content: state.duwu.get(&quot;content&quot;).toJS()
        //调整后，duwu也是一个immutable对象了，获取值的方式需做调整，如下
        title: state.getIn([&quot;duwu&quot;,&quot;title&quot;]).toJS(),
        content: state.getIn([&quot;duwu&quot;,&quot;content&quot;]).toJS()
    }
}
</code></pre>
<h2 id="使用-redux-thunk：进一步从页面中抽离业务方法"><a href="#使用-redux-thunk：进一步从页面中抽离业务方法" class="headerlink" title="使用 redux-thunk：进一步从页面中抽离业务方法"></a>使用 redux-thunk：进一步从页面中抽离业务方法</h2><blockquote>
<p>使用 /pages/index 页面来做演示</p>
<ul>
<li>默认情况下， 在 actionCreators，只能够返回对象，而不能返回方法（ dispatch 的入参至支持对象）</li>
<li>但引入 redux-thunk 之后，dispatch 的入参就可以是一个方法。</li>
<li>何为 redux-thunk ？<ul>
<li>它是 redux 的一个中间件，用以支持 dispatch 的参数是一个方法。</li>
</ul>
</li>
</ul>
</blockquote>
<h3 id="使用-redux-thunk-改写-index-页"><a href="#使用-redux-thunk-改写-index-页" class="headerlink" title="使用 redux-thunk 改写 index 页"></a>使用 redux-thunk 改写 index 页</h3><ul>
<li>安装：</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">npm install --save redux-thunk
</code></pre>
<ul>
<li><p>调整 /store/index.js，引入该中间件<br></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { createStore,applyMiddleware } from &#39;redux&#39;;//引入 applyMiddleware
import reducer from &#39;./reducer&#39;;
import thunk from &#39;redux-thunk&#39;;
//创建store的时候，将中间件thunk作为参数传入
const store = createStore(reducer,applyMiddleware(thunk))
export default store;
</code></pre>
</li>
<li><p>调整 /pages/index 文件内容及目录，新增 store 文件夹，如下：</p>
</li>
</ul>
<p><img src="https://pic.zhuliang.ltd/81d9a1de-a2ce-48c0-ba99-251b7649c09c.png-c" alt="81d9a1de-a2ce-48c0-ba99-251b7649c09c.png"></p>
<ul>
<li>/pages/index/index.js<br><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><pre><code class="javascript">import React, { PureComponent } from &#39;react&#39;
import { setWindowScrollToSession, autoWindowScroll } from &#39;../../common/utility&#39;;
import { connect } from &#39;react-redux&#39;; //引入 connect 模块
import { InitIndex } from &#39;./store/actionCreators&#39;; //引入InitIndex 方法
class Index extends PureComponent {
  render() {
      return &lt;div dangerouslySetInnerHTML={{ __html: this.props.content }}&gt;&lt;/div&gt;
  }
  componentDidMount() {
      this.props.initPage(this.props.location.pathname);
  }
  componentWillUnmount() {
      setWindowScrollToSession(this.props.location.pathname);
  }
}
const mapStateToProps = (state) =&gt; {
  return {
      content: state.getIn([&quot;index&quot;, &quot;content&quot;])
  }
}
const mapDispatchToProps = (dispatch) =&gt; {
  return {
      initPage(pathname) {
          dispatch(InitIndex()); //dispatch中使用的是方法，而非对象
          autoWindowScroll(pathname);
      }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Index);
</code></pre>
</li>
</ul>
<ul>
<li>/pages/index/store/actionCreators.js<br><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><pre><code class="javascript">import { GET_INDEX_TEMPLATE } from &#39;./actionTypes&#39;;
import http from &#39;../../../service/api&#39;;
const GetInitIndexAction = (content) =&gt; {
  return {
      type: GET_INDEX_TEMPLATE,
      data: content
  };
}
export const InitIndex = () =&gt; {
  return (dispatch) =&gt; {
      http.get(&quot;/api/feature.json&quot;).then((result) =&gt; {
          let action = GetInitIndexAction(result.data);
          dispatch(action);
      });
  }
}
</code></pre>
</li>
</ul>
<h2 id="chrome插件：redux的安装和配置及使用"><a href="#chrome插件：redux的安装和配置及使用" class="headerlink" title="chrome插件：redux的安装和配置及使用"></a>chrome插件：redux的安装和配置及使用</h2><p>在 /store/index.js 中增加对 redux-devtools 的支持</p>
<blockquote>
<p>参考： <a href="https://github.com/zalmoxisus/redux-devtools-extension#usage" target="_blank" rel="noopener">https://github.com/zalmoxisus/redux-devtools-extension#usage</a></p>
</blockquote>
<p>在使用了中间件的情况下，使用以下代码：</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">const composeEnhancers =
  typeof window === &#39;object&#39; &amp;&amp;
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
      // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
    }) : compose;

const enhancer = composeEnhancers(
  applyMiddleware(...middleware),
  // other store enhancers if any
);
const store = createStore(reducer, enhancer);
</code></pre>
<p>最终调整后的 /store/index.js 的代码为：</p>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">import { createStore,applyMiddleware,compose } from &#39;redux&#39;;
import reducer from &#39;./reducer&#39;;
import thunk from &#39;redux-thunk&#39;;

const composeEnhancers =
  typeof window === &#39;object&#39; &amp;&amp;
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
    }) : compose;

const enhancer = composeEnhancers(
  applyMiddleware(thunk),
);

const store = createStore(reducer,enhancer)

export default store;
</code></pre>
<p>加入支持前：</p>
<p><img src="https://pic.zhuliang.ltd/20faf66c-8d1c-40ec-8bc7-76b0bc6e92bb.png-c" alt="20faf66c-8d1c-40ec-8bc7-76b0bc6e92bb.png"></p>
<p>加入支持后：</p>
<p><img src="https://pic.zhuliang.ltd/2b6652be-2f08-4737-b209-5adc4bcaf35e.png-c" alt="2b6652be-2f08-4737-b209-5adc4bcaf35e.png"></p>
<h1 id="2018-11-16-培训内容：使用公司内部-npm-，ui-组件化培训"><a href="#2018-11-16-培训内容：使用公司内部-npm-，ui-组件化培训" class="headerlink" title="2018-11-16 培训内容：使用公司内部 npm ，ui 组件化培训"></a>2018-11-16 培训内容：使用公司内部 npm ，ui 组件化培训</h1><h1 id="概述："><a href="#概述：" class="headerlink" title="概述："></a>概述：</h1><ul>
<li>本次培训内容基于 kede.mobile 项目，版本：<a href="https://github.com/KedeIT/kede.mobile/tree/tag/v1.0" target="_blank" rel="noopener">tag/v1.0</a></li>
<li>本次培训内容最终会合并至kede.mobile的master分支，并且打标为：<a href="https://github.com/KedeIT/kede.mobile/tree/tag/v2.0" target="_blank" rel="noopener">tag/v2.0</a></li>
<li>kede.mobile 项目：git tag 版本说明：<ul>
<li>v1.0：kede.mobile培训内容，components组件库包含在项目中</li>
<li>v2.0：kede.mobile培训内容，components组件库作为独立的package存在，kede.mobile安装该组件库</li>
</ul>
</li>
<li>demo-react-ui 项目的 git 地址：<a href="https://github.com/KedeIT/demo-react-ui" target="_blank" rel="noopener">demo-react-ui in git</a></li>
<li>demo-react-ui 组件库的 npm package 放在公司内网中：地址：<a href="http://192.168.117.183:4873/#/detail/demo-react-ui" target="_blank" rel="noopener">http://192.168.117.183:4873/#/detail/demo-react-ui</a><h1 id="如何制作包？"><a href="#如何制作包？" class="headerlink" title="如何制作包？"></a>如何制作包？</h1><h2 id="通过使用-create-react-library-创建组件库"><a href="#通过使用-create-react-library-创建组件库" class="headerlink" title="通过使用 create-react-library 创建组件库"></a>通过使用 create-react-library 创建组件库</h2><blockquote>
<p>create-react-library项目地址：<a href="https://github.com/transitive-bullshit/create-react-library" target="_blank" rel="noopener">create-react-library in github</a></p>
</blockquote>
</li>
</ul>
<ol>
<li>安装：create-react-library<br><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><pre><code class="javascript">npm install -g create-react-library
</code></pre>
</li>
<li>通过 create-react-library 创建组件 lib</li>
</ol>
<ul>
<li>首先 cd 到对应的目录</li>
<li>执行：<br><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><pre><code class="javascript">create-react-library
</code></pre>
</li>
<li>之后根据问答式提示进行创建包文件，如：<br><img src="https://pic.zhuliang.ltd/cc756c40-7e9d-4437-81ac-c17886075916.png-c" alt="cc756c40-7e9d-4437-81ac-c17886075916.png"><blockquote>
<p>tips：安装模板的时候会从 npm 源下载一些文件，建议先将 npm 的 registry 切到国内，如淘宝源等。</p>
</blockquote>
</li>
</ul>
<p>生成后目录结构：<br>/</p>
<p><img src="https://pic.zhuliang.ltd/807fa51f-a736-42ad-a600-5c70b5045f6d.png-c" alt="807fa51f-a736-42ad-a600-5c70b5045f6d.png"></p>
<p>其中：</p>
<ul>
<li><p>/dist：发布组件到npm的时候，会将生成到此文件夹中的文件发布上去（即包的内容） </p>
</li>
<li><p>/src：我们自定义的ui组件源码放在该目录下</p>
</li>
</ul>
<p><img src="https://pic.zhuliang.ltd/cd68fc76-8a1b-46f2-a351-2c3e5abb24a9.png-c" alt="cd68fc76-8a1b-46f2-a351-2c3e5abb24a9.png"></p>
<ul>
<li>/example：主要用于测试我们编写的组件</li>
</ul>
<p><img src="https://pic.zhuliang.ltd/d294c2d5-01fd-416f-b208-174d2f8184bc.png-c" alt="d294c2d5-01fd-416f-b208-174d2f8184bc.png"></p>
<h2 id="添加ui组件源码"><a href="#添加ui组件源码" class="headerlink" title="添加ui组件源码"></a>添加ui组件源码</h2><p>将 kede.mobile 项目中的 assets，components 文件夹及其中的内容复制到 src 文件夹内，如：</p>
<p><img src="https://pic.zhuliang.ltd/3515dec6-fab1-4e60-aa8c-d7a9a27231f7.png-c" alt="3515dec6-fab1-4e60-aa8c-d7a9a27231f7.png"></p>
<ul>
<li>其中 /src/index.js 作为当前组件的入口函数，其内容为：</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">/*      /src/index.js     */
import Header from &#39;./Header&#39;;
import Footer from &#39;./Footer&#39;;
import GoToTop from &#39;./GoToTop&#39;;

export { Header, Footer, GoToTop };
</code></pre>
<h2 id="安装必要的模块"><a href="#安装必要的模块" class="headerlink" title="安装必要的模块"></a>安装必要的模块</h2><blockquote>
<p>本次需要安装的模块有： styled-components、react-router-dom</p>
</blockquote>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">/*为防止冲突，这里安装模块的时候使用 -D 选项，同时修改package.json，将 styled-components，react-router-dom添加到 peerDependencies中 */
npm install -D styled-components react-router-dom
</code></pre>
<pre><code class="javascript">/* package.json  代码片段*/

&quot;peerDependencies&quot;: {
    /*其他模块信息*/

&quot;styled-components&quot;: &quot;^4.1.1&quot;,
&quot;react-router-dom&quot;: &quot;^4.3.1&quot;
},
&quot;devDependencies&quot;: {

      /*其他模块信息*/

&quot;styled-components&quot;: &quot;^4.1.1&quot;,
&quot;react-router-dom&quot;: &quot;^4.3.1&quot;
}
&lt;p class=&quot;code-caption&quot; data-lang=&quot;&quot; data-line_number=&quot;backend&quot; data-trim_indent=&quot;backend&quot; data-label_position=&quot;outer&quot; data-labels_left=&quot;Code&quot; data-labels_right=&quot;:&quot; data-labels_copy=&quot;Copy Code&quot;&gt;&lt;span class=&quot;code-caption-label&quot;&gt;&lt;/span&gt;&lt;/p&gt;
</code></pre>
<h2 id="发布前的测试"><a href="#发布前的测试" class="headerlink" title="发布前的测试"></a>发布前的测试</h2><blockquote>
<p>调整 /example 中的项目，以可以显示安装的 ui 组件，对于本次培训，直接调整 /example/src/index.js 即可：</p>
</blockquote>
<pre><code class="javascript">/*  example/src/index.js  */
import React, { Fragment } from &#39;react&#39;
import ReactDOM from &#39;react-dom&#39;
import { BrowserRouter } from &#39;react-router-dom&#39;; 
import &#39;./index.css&#39;
import App from &#39;./App&#39;
const container = (
    &lt;Fragment&gt;
        &lt;BrowserRouter&gt;
            &lt;App /&gt;
        &lt;/BrowserRouter&gt;
    &lt;/Fragment&gt;
)
ReactDOM.render(container, document.getElementById(&#39;root&#39;))
</code></pre>
<ul>
<li>测试：</li>
</ul>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">/*
需要启动两个项目：
    1.在根目录下：npm start：这里主要是为了能够监听文件的变化，以可以实时打包编译至dist文件夹中
    2.cd到example文件夹：npm start：这里就是启动一个dev-server，用于测试
*/
//在项目根目录下：
npm start
//定位到example文件夹
cd example
npm start
</code></pre>
<h1 id="如何上传包？"><a href="#如何上传包？" class="headerlink" title="如何上传包？"></a>如何上传包？</h1><blockquote>
<p>公司内部私有库地址：<a href="http://192.168.117.183:4873/" target="_blank" rel="noopener">http://192.168.117.183:4873/</a><br>基于 verdaccio：一个轻量级的私有代理 npm 库</p>
</blockquote>
<h2 id="通过添加一个-registry"><a href="#通过添加一个-registry" class="headerlink" title="通过添加一个 registry"></a>通过添加一个 registry</h2><blockquote>
<p>不强制，只是个人习惯而已，大家有其他好的方法的，记得分享 ：）</p>
<ul>
<li>这里通过 nrm 来添加 registry</li>
</ul>
</blockquote>
<p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">nrm add kede http://192.168.117.183:4873/

测试：
nrm test kede

将npm的当前registry切换至kede
nrm use kede
</code></pre>
<blockquote>
<p><strong>P.S.：</strong> 目前，在 verdaccio 配置中，uplink已经设置为 taobao 源，但测试下来，虽然已经设置了上游信息，在安装非内部包的时候依然会出现一些莫名错误。建议：在安装非内部pakcage的时候，切换到 taobao 源进行安装。</p>
</blockquote>
<p><strong>以下步骤均在 kede 源下进行：$nrm use kede</strong></p>
<h2 id="注册："><a href="#注册：" class="headerlink" title="注册："></a>注册：</h2><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">npm adduser
//之后根据提示进行注册
</code></pre>
<h2 id="登陆："><a href="#登陆：" class="headerlink" title="登陆："></a>登陆：</h2><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">npm login
//之后根据提示进行注册
</code></pre>
<h2 id="发布："><a href="#发布：" class="headerlink" title="发布："></a>发布：</h2><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">npm publish
</code></pre>
<h2 id="取消发布："><a href="#取消发布：" class="headerlink" title="取消发布："></a>取消发布：</h2><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">npm unpublish &lt;package_name&gt;@&lt;version_no&gt;
</code></pre>
<h1 id="在-kede-mobile-中使用该-package"><a href="#在-kede-mobile-中使用该-package" class="headerlink" title="在 kede.mobile 中使用该 package"></a>在 kede.mobile 中使用该 package</h1><h2 id="安装-demo-react-ui："><a href="#安装-demo-react-ui：" class="headerlink" title="安装 demo-react-ui："></a>安装 demo-react-ui：</h2><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">nrm use kede
npm install --save demo-react-ui
</code></pre>
<h2 id="删除项目中的-components-文件夹"><a href="#删除项目中的-components-文件夹" class="headerlink" title="删除项目中的 components 文件夹"></a>删除项目中的 components 文件夹</h2><h2 id="调整引用："><a href="#调整引用：" class="headerlink" title="调整引用："></a>调整引用：</h2><p></p><p class="code-caption" data-lang="javascript" data-line_number="backend" data-trim_indent="backend" data-label_position="outer" data-labels_left="Code" data-labels_right=":" data-labels_copy="Copy Code"><span class="code-caption-label"></span></p><p></p>
<pre><code class="javascript">/*  src\App.js */

// import Header from &#39;./components/header&#39;;
// import Footer from &#39;./components/footer&#39;;
import {Header, Footer} from &#39;demo-react-ui&#39;;
</code></pre>
<pre><code class="javascript">/*  src\pages\cart\index.js */

// import GoToTop from &#39;../../components/goToTop&#39;;
import {GoToTop} from &#39;demo-react-ui&#39;;
</code></pre>

      

      <div>
        
        <div style="background-color: rgb(255, 23, 0);user-select: auto;height: 3px;width:100%;margin-top: 40px;margin-bottom:15px;"></div>

<!-- <p>常言道：学然后知不足，教然后知困。</p>
<p>我知道你的焦虑，一起共进加油：P</p>
<p>关不关注都无所谓，会根据生活节奏紧凑度定期分享些开发经验、搬砖生涯、痛点、感悟。</p>
<p>
        <img src="https://pic.zhuliang.ltd/20191213133908.gif" alt="欢迎关注我的订阅号：P" />
</p> -->
<ul class="post-copyright" style="margin-left:0px;">
    <li class="post-copyright-author">
        <strong>本文作者：</strong>积慕
    </li>
    <li class="post-copyright-link">
        <strong>本文链接：</strong>
        <a href="/2018/11/frontend/hello-react-kede-mobile.html" title="部门内部培训 react 系列讲义二：使用 React 重写 kede mobile">https://blog.zhuliang.ltd/2018/11/frontend/hello-react-kede-mobile.html</a>
    </li>
    <li class="post-copyright-license">
        <strong>版权： </strong>
        本站文章均采用 <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/cn/" rel="external nofollow"
            target="_blank">CC BY-NC-SA 3.0 CN</a> 许可协议，请勿用于商业，转载注明出处！
    </li>
</ul>
        
      </div>
    </div>

    <footer>
      
      
  
  <div class="categories">
    <a href="/categories/frontend/">frontend</a>
  </div>

      
  
  <div class="tags">
    <a href="/tags/教程/">教程</a>, <a href="/tags/react/">react</a>, <a href="/tags/培训讲义/">培训讲义</a>
  </div>

      <!-- 
  <div class="addthis addthis_toolbox addthis_default_style">
    
    
      <a class="addthis_button_tweet"></a>
    
    
      <a class="addthis_button_google_plusone" g:plusone:size="medium"></a>
    
    
      <a class="addthis_button_pinterest_pinit" pi:pinit:layout="horizontal"></a>
    
    <a class="addthis_counter addthis_pill_style"></a>
  </div>
  <script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js"></script>
 -->
      
      <div class="clearfix"></div>
    </footer>
  </div>
</article>

<!-- 
<section id="comment">
  <h1 class="title">留言</h1>

  <div id="vcomments" style="background-color: #fff"></div>
  <script>
    new Valine({
      el: '#vcomments',
      appId: 'aTPKmG6tjDo3CbIXzAPzrj4X-gzGzoHsz',
      appKey: 'hNgWFneNpEcMnBJclPQnDgsd',
      notify: true,
      verify: true, //验证码
      avatar: 'monsterid', //https://valine.js.org/avatar.html
      placeholder: '请输入你的评论'
    })
  </script>
</section>
 --> <!--启用/关闭 LeanCloud 评论--></div></div>
    <aside id="sidebar" class="alignright">
  
<div class="widget tag">
  <h3 class="title"><i class="iconfont icon-category"></i>&nbsp;&nbsp;分类</h3>
  <ul class="entry">
  
    <li><a href="/categories/Obsidian/">Obsidian</a><small>1</small></li>
  
    <li><a href="/categories/PowerUser/">PowerUser</a><small>4</small></li>
  
    <li><a href="/categories/backend/">backend</a><small>57</small></li>
  
    <li><a href="/categories/frontend/">frontend</a><small>9</small></li>
  
    <li><a href="/categories/operations/">operations</a><small>5</small></li>
  
    <li><a href="/categories/提前退休/">提前退休</a><small>2</small></li>
  
  </ul>
</div>


  
<div class="widget tag">
  <h3 class="title"><i class="iconfont icon-new3"></i>&nbsp;&nbsp;最新文章</h3>
  <ul class="entry">
    
      <li>
        <a href="/2024/11/PowerUser/install-docker-in-pve-lxc.html">1. 通过 PVE LXC 搭建 docker 环境</a>
      </li>
    
      <li>
        <a href="/2024/11/Obsidian/obsidian-render-pie-chart.html">2. 2 套用于 obsidian 的生成时间占用饼图脚本示例，基于 templater， charts，echarts+dataview</a>
      </li>
    
      <li>
        <a href="/2024/10/PowerUser/fix-ios18-samba-readonly-issue.html">3. 解决 ios 18（包括ipadOS 18） 下通过文件(files app)连接 samba 服务器，目录变成只读权限的问题</a>
      </li>
    
      <li>
        <a href="/2023/07/operations/how-to-use-pm2.html">4. PM2 的安装、配置和使用</a>
      </li>
    
      <li>
        <a href="/2023/07/backend/expose-intranet-netcoreapi-to-internet.html">5. 通过 DDNS+显性URL 把内网部署的.NET CORE WEBAPI 项目暴露到公网中</a>
      </li>
    
  </ul>
</div>


  
<div class="widget tag">
  <h3 class="title"><i class="iconfont icon-tag"></i>&nbsp;&nbsp;标签</h3>
  <ul class="entry">
  
    <li><a href="/tags/net-core/">.net core</a><small>12</small></li>
  
    <li><a href="/tags/Debian/">Debian</a><small>2</small></li>
  
    <li><a href="/tags/Docker/">Docker</a><small>1</small></li>
  
    <li><a href="/tags/Elasticsearch/">Elasticsearch</a><small>35</small></li>
  
    <li><a href="/tags/Elasticsearch-Nest系列/">Elasticsearch.Nest系列</a><small>35</small></li>
  
    <li><a href="/tags/Obsidian/">Obsidian</a><small>1</small></li>
  
    <li><a href="/tags/PVE/">PVE</a><small>2</small></li>
  
    <li><a href="/tags/StackExchange-Redis系列/">StackExchange.Redis系列</a><small>12</small></li>
  
    <li><a href="/tags/centos/">centos</a><small>3</small></li>
  
    <li><a href="/tags/flex/">flex</a><small>1</small></li>
  
    <li><a href="/tags/ios/">ios</a><small>1</small></li>
  
    <li><a href="/tags/javascript/">javascript</a><small>1</small></li>
  
    <li><a href="/tags/linux/">linux</a><small>4</small></li>
  
    <li><a href="/tags/nginx/">nginx</a><small>2</small></li>
  
    <li><a href="/tags/nuget/">nuget</a><small>2</small></li>
  
    <li><a href="/tags/react/">react</a><small>6</small></li>
  
    <li><a href="/tags/styled-components/">styled-components</a><small>1</small></li>
  
    <li><a href="/tags/内网穿透/">内网穿透</a><small>4</small></li>
  
    <li><a href="/tags/培训讲义/">培训讲义</a><small>2</small></li>
  
    <li><a href="/tags/基础知识/">基础知识</a><small>2</small></li>
  
    <li><a href="/tags/性能优化/">性能优化</a><small>2</small></li>
  
    <li><a href="/tags/教程/">教程</a><small>64</small></li>
  
    <li><a href="/tags/理财/">理财</a><small>2</small></li>
  
    <li><a href="/tags/生命周期/">生命周期</a><small>1</small></li>
  
    <li><a href="/tags/经验/">经验</a><small>4</small></li>
  
  </ul>
</div>


  
  <script type="text/javascript" charset="utf-8" src="/js/tagcloud.js"></script>
  <script type="text/javascript" charset="utf-8" src="/js/tagcanvas.js"></script>
  <div class="widget tag">
    <h3 class="title"><i class="iconfont icon-cloud1"></i>&nbsp;&nbsp;标签云</h3>
    <div id="myCanvasContainer" class="widget tagcloud">
      <canvas width="250" height="250" id="resCanvas" style="width:100%">
        <a href="/tags/net-core/" style="font-size: 17.14px;">.net core</a> <a href="/tags/Debian/" style="font-size: 11.43px;">Debian</a> <a href="/tags/Docker/" style="font-size: 10px;">Docker</a> <a href="/tags/Elasticsearch/" style="font-size: 18.57px;">Elasticsearch</a> <a href="/tags/Elasticsearch-Nest系列/" style="font-size: 18.57px;">Elasticsearch.Nest系列</a> <a href="/tags/Obsidian/" style="font-size: 10px;">Obsidian</a> <a href="/tags/PVE/" style="font-size: 11.43px;">PVE</a> <a href="/tags/StackExchange-Redis系列/" style="font-size: 17.14px;">StackExchange.Redis系列</a> <a href="/tags/centos/" style="font-size: 12.86px;">centos</a> <a href="/tags/flex/" style="font-size: 10px;">flex</a> <a href="/tags/ios/" style="font-size: 10px;">ios</a> <a href="/tags/javascript/" style="font-size: 10px;">javascript</a> <a href="/tags/linux/" style="font-size: 14.29px;">linux</a> <a href="/tags/nginx/" style="font-size: 11.43px;">nginx</a> <a href="/tags/nuget/" style="font-size: 11.43px;">nuget</a> <a href="/tags/react/" style="font-size: 15.71px;">react</a> <a href="/tags/styled-components/" style="font-size: 10px;">styled-components</a> <a href="/tags/内网穿透/" style="font-size: 14.29px;">内网穿透</a> <a href="/tags/培训讲义/" style="font-size: 11.43px;">培训讲义</a> <a href="/tags/基础知识/" style="font-size: 11.43px;">基础知识</a> <a href="/tags/性能优化/" style="font-size: 11.43px;">性能优化</a> <a href="/tags/教程/" style="font-size: 20px;">教程</a> <a href="/tags/理财/" style="font-size: 11.43px;">理财</a> <a href="/tags/生命周期/" style="font-size: 10px;">生命周期</a> <a href="/tags/经验/" style="font-size: 14.29px;">经验</a>
      </canvas>
    </div>
  </div>




</aside>
    <div class="clearfix"></div>
  </div>
  <footer id="footer" class="inner"><div class="alignleft">

  
  &copy; 2018 - 2024 &nbsp;<a href='/about'>积慕</a>
  
  &nbsp;&nbsp;<a href='https://www.cnblogs.com/deepthought/'>我的博客园地址</a>
  &nbsp;&nbsp;finalclassic#outlook.com
</div>

<div class="clearfix"></div>
<div>
  本站主题基于：<a href='https://github.com/hexojs/hexo-theme-light' rel="external nofollow">hexo-theme-light</a>
</div>
<div id="busuanzi_container_site_pv">
  本站总访问量&nbsp;<span id="busuanzi_value_site_pv"></span>&nbsp;次
</div>
<div style="width:300px;margin:0 auto; padding:20px 0;">
  <a target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=31011702004805" style="display:inline-block;text-decoration:none;height:20px;line-height:20px;">
    <img src="https://pic.zhuliang.ltd/20200102164648.png" style="float:left;"><p style="float:left;height:20px;line-height:20px;margin: 0px 0px 0px 5px; color:#939393;">沪公网安备 31011702004805号</p></a>
</div></footer>
  <script src="/js/jquery-2.0.3.min.js"></script>
<script src="/js/jquery.imagesloaded.min.js"></script>
<script src="/js/gallery.js"></script>




<link rel="stylesheet" href="/fancybox/jquery.fancybox.css" media="screen" type="text/css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script type="text/javascript">
(function($){
  $('.fancybox').fancybox();
})(jQuery);
</script>


<script type="text/javascript">
hljs.initHighlightingOnLoad();
</script>

<!-- <script src="/js/busuanzi.pure.mini.js"></script> -->
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<script>
    var _hmt = _hmt || [];
    (function () {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?ea793b9c8d6b31df666a6bc15038e246";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
    })();
</script>
<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        }
        else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>
</body>
</html>
